/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       town_window.h

	$Header: /game/town_window.h $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "town_window.h"

#include "abbreviate_number.h"
#include "abstract_adventure_map.h"
#include "adaptor_handler.h"
#include "adjusted_animation_cache.h"
#include "adjusted_bitmap_cache.h"
#include "adv_actor_model.h"
#include "adv_object_strings.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_sounds.h"
#include "animated_window.h"
#include "animation_cache.h"
#include "army_dialog_base.h"
#include "artifact_effect_type.h"
#include "bitmap_group_cache.h"
#include "bitmap_group_cache_data.h"
#include "bitmap_layer_cache_window.h"
#include "bitmap_layer_window.h"
#include "blacksmith_window.h"
#include "blended_bitmap_window.h"
#include "bound_handler.h"
#include "building_traits.h"
#include "button.h"
#include "button_cache.h"
#include "buy_building_window.h"
#include "castle_window.h"
#include "cheats.h"
#include "creature_array_window.h"
#include "creature_traits.h"
#include "dialog_caravan_arrivals.h"
#include "dialog_caravan_creation.h"
#include "dialog_caravan_initial.h"
#include "dialog_caravan_view.h"
#include "dialog_choose_lord.h"
#include "dialog_creature_portal.h"
#include "dialog_mana_vortex.h"
#include "elements_of.h"
#include "enum_operations.h"
#include "foreign_creature_array_window_controls.h"
#include "format_string.h"
#include "function_handler.h"
#include "game_application.h"
#include "game_replace_keywords.h"
#include "game_time.h"
#include "hero.h"
#include "hero_class_properties.h"
#include "hero_keyword_replacer.h"
#include "hire_hero_dialog.h"
#include "idle_processor.h"
#include "interface_help.h"
#include "keyword_replacer.h"
#include "mage_guild_window.h"
#include "material_icons.h"
#include "material_names.h"
#include "music.h"
#include "options.h"
#include "platform.h"
#include "player.h"
#include "progress_bar_dialog.h"
#include "progress_handler.h"
#include "recruit_dialog.h"
#include "replace_keywords.h"
#include "resource.h"
#include "scale_bitmap.h"
#include "scaled_animation_cache.h"
#include "scroll_menu.h"
#include "scrollbar.h"
#include "seminary_window.h"
#include "skeleton_transformer_window.h"
#include "skill.h"
#include "skill_properties.h"
#include "simple_dialog.h"
#include "sound_cache.h"
#include "standard_fonts.h"
#include "text_window.h"
#include "town_drawing.h"
#include "town_list_window.h"
#include "town_prison_window.h"


extern t_button_cache		g_ok_button;
t_button_cache				g_divide_button( "button.split" );
t_button_cache				g_divide_single_button( "button.split_single" );
extern t_button_cache const k_move_garrison_down_button( "button.move_garrison_down" );
extern t_button_cache const k_move_up_button( "button.move_up" );
extern t_bitmap_group_cache g_creature_rings;
extern t_external_string const k_buy_creatures_not_owner( "town.buy_creatures_not_owner" );
extern t_external_string const k_buy_building_not_owner( "town.buy_building_not_owner" );
extern t_bitmap_group_cache g_small_town_list_bitmaps;


// basic interface elements
t_bitmap_group_cache g_interface_800(  "town.0800" );
t_bitmap_group_cache g_interface_1024( "town.1024" );
t_bitmap_group_cache g_interface_1280( "town.1280" );

namespace { // unnamed namespace
									  
	// hotkey resource strings
	t_external_string const k_hot_key_purchase_building( "purchase_building.hot_keys" );	// B
	t_external_string const k_hot_key_hire_hero( "hire_hero.hot_keys" );					// H
	t_external_string const k_hot_key_recruit_creature( "recruit_creature.hot_keys" );		// C
	t_external_string const k_hot_key_magic_guild( "mage_guild.hot_keys" );					// G
	t_external_string const k_hot_key_split_creatures( "split_creatures.hot_keys" );		// S
	t_external_string const k_hot_key_lordship( "lordship.hot_keys" );						// L
	t_external_string const k_hot_key_view_caravan( "view_caravan_town.hot_keys" );			// A
	t_external_string const k_hot_key_marketplace( "marketplace.hot_keys" );				// M

	t_counted_ptr<t_dialog_cheat> g_cheats_window;
	t_sound_cache g_current_music;	// used to memorize the music that was playing before

	// ----------------------------------------------------
	// set music for town type
	// ----------------------------------------------------
	void set_town_music( t_town_type town )
	{
		static t_sound_cache const k_town_music[] =
		{	
			"town.life", 
			"town.order", 
			"town.death", 
			"town.chaos", 
			"town.nature", 
			"town.might"
		};

		g_current_music = get_music_playing();

		assert( town >= 0 && town < k_town_type_count );
		play_music( k_town_music[town] );
	};

	// ----------------------------------------------------

	// --------------------------------------------------------
	// specialized cache just for town layouts - must record information about
	// layer positions before scaling
	// --------------------------------------------------------
	class t_layout_cache_data : public t_bitmap_group_cache_data
	{
	public:
		// Constructor/Destructor
		t_layout_cache_data( std::string const & name, double scale );

		t_screen_point get_unscaled_origin( std::string const& name ) const;
	private:
		struct t_layer_origin
		{
			std::string    name;
			t_screen_point origin;
		};

		struct t_find_origin
		{
			bool operator()( t_layer_origin const& data, std::string const& name )
			{
				return stricmp( data.name.c_str(), name.c_str() ) < 0;
			}
		};

		// Member functions
		virtual t_bitmap_group* do_read( std::streambuf & stream, t_progress_handler* handler );

		std::vector<t_layer_origin> m_origins;
		double						m_scale;
	};

	inline t_layout_cache_data::t_layout_cache_data( std::string const & name, double scale )
		                      : t_bitmap_group_cache_data( name )
	{
		m_scale = scale;
	}

	class t_layout_cache : public t_bitmap_group_cache
	{
	protected:
		t_layout_cache_data* m_data;
	public:
		t_layout_cache()
		{
		}
		t_layout_cache( std::string const & name, double scale ) : t_bitmap_group_cache()
		{
			m_data = new t_layout_cache_data( name, scale );
			set( m_data );
		}

		t_screen_point get_unscaled_origin( std::string const& name ) const
		{
			return m_data->get_unscaled_origin( name );
		}
	};


	// --------------------------------------------------------
	// town building layouts 
	// --------------------------------------------------------
	const int k_highlight_value = 256 * 3 / 2;

	class t_layout_array
	{
	public:
		t_layout_array( double scale );

		double             get_scale() const;
		t_bitmap_group_ptr get( t_town_type town ) const;
		t_layout_cache     get_cache( t_town_type town ) const;
		t_bitmap_group_ptr get_highlight( t_town_type town ) const;
	protected:
		t_layout_cache			m_caches[k_town_type_count];
		t_adjusted_bitmap_cache m_highlights[k_town_type_count];
		double					m_scale;
	};

	inline double t_layout_array::get_scale() const
	{
		return m_scale;
	}

	inline t_bitmap_group_ptr t_layout_array::get( t_town_type town ) const
	{
		return m_caches[town].get();
	}

	inline t_layout_cache t_layout_array::get_cache( t_town_type town ) const
	{
		return m_caches[town];
	}

	inline t_bitmap_group_ptr t_layout_array::get_highlight( t_town_type town ) const
	{
		return m_highlights[town].get();
	}

	// ---------------------------------------------------------------------------
	// class which stores backdrop caches for various towns at various scales
	// ---------------------------------------------------------------------------
	class t_backdrop_caches 
	{
	public:
		t_backdrop_caches( double scale );

		t_bitmap_group_cache* operator[]( t_town_type town );
		double                get_scale() const;
	protected:
		double               m_scale;
		t_bitmap_group_cache m_caches[k_town_type_count][k_terrain_count];
	};

	// ---------------------------------------------------------------------------
	// inlines for t_backdrop_caches
	// ---------------------------------------------------------------------------
	inline t_bitmap_group_cache* t_backdrop_caches::operator[]( t_town_type town )
	{
		return m_caches[town];
	}

	inline double t_backdrop_caches::get_scale() const
	{
		return m_scale;
	}

	// ------------------------------------------------------------
	// animations for various towns
	// ------------------------------------------------------------
	class t_town_animation_array
	{
	public:
		t_town_animation_array( double scale );

		double					   get_scale() const;
		t_scaled_animation_cache   get_cache( t_town_type town, t_town_building building ) const;
		t_adjusted_animation_cache get_highlight( t_town_type town, t_town_building building ) const;
	protected:
		double					   m_scale;
		t_scaled_animation_cache   m_caches[k_town_type_count][k_town_building_count];
		t_adjusted_animation_cache m_highlights[k_town_type_count][k_town_building_count];
	};

	inline double t_town_animation_array::get_scale() const
	{
		return m_scale;
	}

	inline t_scaled_animation_cache t_town_animation_array::get_cache( t_town_type town,
																	   t_town_building building ) const
	{
		return m_caches[town][building];
	}

	inline 
	t_adjusted_animation_cache t_town_animation_array::get_highlight( t_town_type town,
																	  t_town_building building ) const
	{
		return m_highlights[town][building];
	}

	
	// timer object
	const int k_frame_time = 50;

	class t_animator : public t_idle_processor
	{
	public:
		t_animator( t_town_window* window );

		virtual void on_idle();
	protected:
		t_town_window* m_window;
	};
}; // end unnamed namespace

// ----------------------------------------------------
// get screen scale
// ----------------------------------------------------
const double get_scale_factor()
{
	return get_main_window_rect().width() / 1280.0;
}

// ---------------------------------------------------------------------
// initialize group of backdrop caches
// ---------------------------------------------------------------------
t_backdrop_caches::t_backdrop_caches( double scale )
{
	t_town_type    town;
	t_terrain_type terrain;
	std::string    name;

	m_scale = scale;
	for (town = t_town_type(0); town < k_town_type_count; enum_incr( town ))
	{
		for (terrain = k_terrain_grass; terrain <= k_terrain_subterranean; enum_incr(terrain))
		{
			name = "town.";
			name += k_town_keyword[town];
			name += ".";
			name += k_terrain_keyword[terrain];
			m_caches[town][terrain] = t_bitmap_group_cache( name, scale );
		}
		while (terrain < k_terrain_count)
		{
			m_caches[town][terrain] = m_caches[town][k_terrain_grass];
			enum_incr(terrain);
		}
		m_caches[town][k_terrain_water] = m_caches[town][k_terrain_sand];
		m_caches[town][k_terrain_water_river] = m_caches[town][k_terrain_sand];
		m_caches[town][k_terrain_lava_river] = m_caches[town][k_terrain_lava];
		m_caches[town][k_terrain_ice_river] = m_caches[town][k_terrain_snow];
	}
}

// ---------------------------------------------------------------------
// initialize group of layout caches
// ---------------------------------------------------------------------
t_layout_array::t_layout_array( double scale )
{
	t_town_type town;
	std::string name;

	m_scale = scale;
	for (town = t_town_type(0); town < k_town_type_count; enum_incr( town ))
	{
		name = "town.";
		name += k_town_keyword[town];
		name += ".layout";
		m_caches[town] = t_layout_cache( name, scale );
		m_highlights[town] = t_adjusted_bitmap_cache( m_caches[town], k_highlight_value );
	}
}


// ---------------------------------------------------------------------
// get a given layout
// ---------------------------------------------------------------------
static t_layout_array& get_layout_array()
{
	static std::vector<t_layout_array> caches;
	int    i;
	double scale = get_scale_factor();

	for (i = 0; i < caches.size(); i++)
		if (scale == caches[i].get_scale())
			break;
	if (i == caches.size())
		caches.push_back( t_layout_array( scale ));
	return caches[i];
}

// ---------------------------------------------------------------------
// get a given layout
// ---------------------------------------------------------------------
inline t_layout_cache get_layout_cache( t_town_type town )
{
	return get_layout_array().get_cache( town );
}

inline t_bitmap_group_ptr get_highlight( t_town_type town )
{
	return get_layout_array().get_highlight( town );
}

// ---------------------------------------------------------------------
// get a given backdrop
// ---------------------------------------------------------------------
static t_bitmap_group_cache& get_backdrop( t_town_type town, t_terrain_type terrain, 
										   double scale )
{
	static std::vector<t_backdrop_caches> caches;
	int i;

	for (i = 0; i < caches.size(); i++)
		if (scale == caches[i].get_scale())
			break;
	if (i == caches.size())
		caches.push_back( t_backdrop_caches( scale ));
	return caches[i][town][terrain];
}


// ------------------------------------------------------------
// animations for various towns
// ------------------------------------------------------------
t_town_animation_array::t_town_animation_array( double scale )
{
	t_town_building building;
	std::string		name;
	t_town_type		town;
	std::string     prefix;

	m_scale = scale;
	for (town = t_town_type(0); town < k_town_type_count; enum_incr( town ))
	{
		prefix = "town.";
		prefix += k_town_keyword[town];
		prefix += ".";
		for (building = t_town_building(0); building < k_town_building_count; enum_incr(building))
		{
			if (!is_legal( town, building ) && building != k_town_grail)
				continue;
			name = prefix;
			name += get_traits( town, building ).keyword;
			m_caches[town][building] = t_scaled_animation_cache( name, scale );
			m_highlights[town][building] 
				= t_adjusted_animation_cache( m_caches[town][building], k_highlight_value );
		}
	}
}

static t_town_animation_array& get_animation_array()
{
	static std::list<t_town_animation_array>    animations;
	double										scale = get_scale_factor(); 
	std::list<t_town_animation_array>::iterator index;

	for (index = animations.begin(); index != animations.end(); index++)
	{
		if (index->get_scale() == scale)
			break;
	}
	if (index == animations.end())
	{
		animations.push_front( t_town_animation_array( scale ));
		index = animations.begin();
	}
	return *index;
}		

inline t_scaled_animation_cache get_animation( t_town_type town, t_town_building building )
{
	return get_animation_array().get_cache( town, building );
}

inline t_adjusted_animation_cache get_highlight_animation( t_town_type town, 
														   t_town_building building )
{
	return get_animation_array().get_highlight( town, building );
}


// ---------------------------------------------------------------------
// get a scaled bitmap, and record the origins of the unscaled bitmap
// ---------------------------------------------------------------------
t_screen_point t_layout_cache_data::get_unscaled_origin( std::string const& name ) const
{
	std::vector<t_layer_origin>::const_iterator index;

	index = std::lower_bound( m_origins.begin(), m_origins.end(), name, t_find_origin() );
	if (index == m_origins.end())
		return t_screen_point( 0, 0 );
	return index->origin;
}

// ---------------------------------------------------------------------
// get a scaled bitmap, and record the origins of the unscaled bitmap
// ---------------------------------------------------------------------
t_bitmap_group* t_layout_cache_data::do_read( std::streambuf & stream, t_progress_handler* handler )
{
	t_bitmap_group*   result = 0;
	t_bitmap_group_24 source;

	if ( source.read( stream, handler ))
	{
		t_screen_rect screen_size = get_main_window_rect();

		// save origins
		if (m_origins.size() == 0)
		{
			t_bitmap_group_24::iterator index = source.begin();
			t_layer_origin              origin;

			while (index != source.end())
			{
				origin.name = index->get_name();
				origin.origin = index->get_rect().top_left();
				m_origins.push_back( origin );
				index++;
			}
		}
		if ( m_scale == 1.0 )
			result = new t_bitmap_group( source );
		else
		{
			result = new t_bitmap_group;
			scale_group( source, *result, m_scale );
		}
	}
	return result;
}


// ---------------------------------------------------------------------
// animate the opening.
// ---------------------------------------------------------------------
t_animator::t_animator( t_town_window* window ) : m_window( window ),
            t_idle_processor(0, k_frame_time )

{
}

void t_animator::on_idle()
{
	m_window->do_fade();
}


namespace 
{
	// --------------------------------------------------------------------
	// choose a nearby visiting army
	// --------------------------------------------------------------------
	class t_visiting_army_selector
	{
		public:
			t_army* choose_visiting_army( t_town* town );

		private:
			void choose_visiting_army_helper( t_adv_map_point const &point, t_adv_map_point const & trigger_cell, bool & stop );

			t_adventure_map* m_map;
			t_town * m_town;
			t_army * m_result;
	};

}

// --------------------------------------------------------------------
// choose a nearby visiting army
// --------------------------------------------------------------------
t_army* t_visiting_army_selector::choose_visiting_army( t_town* town )
{
	m_map = town->get_map();
	m_town = town;
	m_result = NULL;

	if ( !same_team( m_town->get_owner(), &m_map->get_player() ) )
		return NULL; // Can only visit own & allied towns

	t_adv_map_point junk;

	town->scan_adjacent_spaces( bound_handler( *this, &t_visiting_army_selector::choose_visiting_army_helper ), junk );

	return m_result;
}

// --------------------------------------------------------------------
// Called by t_adventure_object::scan_adjacent_spaces to look for 
// an army in this point
// --------------------------------------------------------------------
void t_visiting_army_selector::choose_visiting_army_helper( t_adv_map_point const &point, t_adv_map_point const & trigger_cell, bool & stop )
{
	// look for an army in this square
	t_adventure_tile const&				tile = m_map->get_adv_tile( point );

	const std::vector<int>& triggers = tile.get_triggers(point.on_bridge);
	for (std::vector<int>::const_iterator index = triggers.begin(); index != triggers.end(); ++index)
	{
		t_army* army = dynamic_cast<t_army*>(&m_map->get_adv_object( *index ));

		if (army == 0)
			continue;

		if (army->is_boat())
			continue;	// Boats aren't allowed to "visit" towns.

		if (army->get_owner_number() != m_map->get_player_number())
			continue;

		if ( !same_team( &m_map->get_player(), army->get_owner() ) )
			continue;

		m_result = army;

		if (m_town->get_last_visiting_army().get() == army)
		{
			stop = true;
			return;
		}
	}
}

// --------------------------------------------------------------------
// town screen
// --------------------------------------------------------------------
t_town_window::t_town_window( t_town* town, t_army* adjacent_army, 
							  t_adventure_frame* parent )
             : t_window( k_no_transparency,  parent ),  m_town( 0 )
{

	close_active_sounds(); // close out the active adventure map sounds for this dialog

	m_temp_army = new t_army;
	if (adjacent_army == 0)
	{
		t_visiting_army_selector selector;
		adjacent_army = selector.choose_visiting_army( town );
	}

	t_town_type type = town->get_type();

	// start the town music
	set_town_music( type );

	m_adjacent_army = adjacent_army;
	m_adventure_frame = parent;

	t_bitmap_group_cache* interface_cache;
	t_progress_bar_dialog* progress_bar_dialog;
	t_progress_handler*    progress_handler;

	progress_bar_dialog = new t_progress_bar_dialog( parent );
	progress_handler = progress_bar_dialog->get_handler();
	t_game_application::set_cursor_visible( false );

	switch (parent->get_width())
	{
		case 800:
			interface_cache = &g_interface_800;
			break;

		case 1024:
			interface_cache = &g_interface_1024;
			break;

		case 1280:
			interface_cache = &g_interface_1280;
			break;
	}
	progress_handler->increment_maximum( interface_cache->get_load_cost() );
	progress_handler->increment_maximum( get_load_time( town ) );

	m_interface = interface_cache->get( progress_handler );

	// get interface section
	t_bitmap_layer const* layer;
	t_window*             window;
	t_screen_point        point;

	// place interface background.
	t_bitmap_layer const* interface_image = m_interface->find( "background" );
	t_screen_point        interface_origin = interface_image->get_rect().top_left();

	m_interface_window = new t_bitmap_layer_window( interface_image, t_screen_point(0,0), this );

	// place border strip
	layer = m_interface->find( "border" );
	window = new t_bitmap_layer_window( layer, t_screen_point(0,0), this );
	
	create_buttons();

	// initialize material display.
	m_material_display.init( m_interface, &town->get_map()->get_player(), interface_origin, 
		                     m_interface_window );
	// place name 
	t_screen_rect rect;
	t_screen_rect frame_rect;

	rect = m_interface->find( "town_name" )->get_rect();
	m_town_name = new t_text_window( get_font( rect.height() ), rect, this, "", t_pixel_24( 0,0,0 ) );
	m_town_name->set_center_horizontal();
	m_town_name->set_drop_shadow( false );

	// place town list
	t_scrollbar*  scrollbar;

	rect = m_interface->find( "scrollbar" )->get_rect() - interface_origin;
	scrollbar = new t_scrollbar( rect.top_left(), rect.height(), m_interface_window, 0, 0 );
	rect = m_interface->find( "town_list" )->get_rect() - interface_origin;
	m_town_list = new t_town_list_window( rect, m_interface_window );
	m_town_list->set_select_handler( bound_handler( *this, &t_town_window::set_town ) );
	m_town_list->set_map( *town->get_map() );
	t_player & player = town->get_map()->get_player();
	m_town_list->set_town_list( player.get_towns(), player.get_team() );
	m_town_list->attach( scrollbar );
	frame_rect = g_small_town_list_bitmaps.get()->find( "frame" )->get_rect();
	scrollbar->set_page_size( rect.height() / frame_rect.height() );

	create_creature_detail();
	set_detail_mode( true );

	// initialize window
	rect = m_interface->get_rect();
	init( rect );

	// setup town images
	set_visible( false );
	set_new_town( town, progress_handler );
	m_town_list->select_town( town );
	set_visible( true );

	// create animation timer
	m_animator = new t_animator( this );
	t_game_application::set_cursor_visible( true );
	progress_bar_dialog->close();

	enter_town();
}

// --------------------------------------------------------------------
// town screen
// destructor
// --------------------------------------------------------------------
t_town_window::~t_town_window()
{
	delete m_animator;
}

// --------------------------------------------------------------------
// town screen
// create buttons
// --------------------------------------------------------------------
void t_town_window::create_buttons()
{
    // place OK button
	t_button*			  button;
	t_bitmap_layer const* layer;
	t_screen_point        point;
	t_button_handler	  handler;
	t_screen_point        interface_origin = get_interface_origin();
	t_help_block const&   shared_help = get_help_block( "shared" );
	t_help_block const&   help = get_help_block( "town_screen" );

	layer = m_interface->find( "ok_button" );
	point = layer->get_rect().top_left();
	button = new t_button( g_ok_button.get(), point - interface_origin, m_interface_window );
	button->set_click_handler( bound_handler( *this, &t_town_window::close_click ) );
	set_help( button, shared_help, "ok" );

	// place menu button
	button = create_button( m_interface, "menu_button", m_interface_window, interface_origin );
	button->set_click_handler( bound_handler( *this, &t_town_window::town_menu ) );
	set_help( button, help, "main_menu" );

	// place text detail button
	button = create_button( m_interface, "creature_stats_button", m_interface_window, 
					        interface_origin );
	if (button != 0)
    {
		button->set_click_handler( bound_handler( *this, &t_town_window::show_text_detail ) );
	    set_help( button, help, "stats_button" );
    }
	button = create_button( m_interface, "creature_skills_button", m_interface_window,
		                    interface_origin );
	if (button != 0)
    {
		button->set_click_handler( bound_handler( *this, &t_town_window::show_icon_detail ) );
	    set_help( button, help, "skills_button" );
    }

	// create move up / move down buttons
	point = m_interface->find("move_up_released")->get_rect().top_left() - interface_origin;
	m_move_up_button = new t_button( k_move_up_button.get(), point, m_interface_window );
	handler = bound_handler( *this, &t_town_window::move_up_click );
	m_move_up_button->set_click_handler( handler );
	set_help( m_move_up_button, shared_help, "move_adjacent_to_garrison" );

	point = m_interface->find("move_down_released")->get_rect().top_left() - interface_origin;
	m_move_down_button = new t_button( k_move_garrison_down_button.get(), point, m_interface_window );
	handler = bound_handler( *this, &t_town_window::move_down_click );
	m_move_down_button->set_click_handler( handler );
	set_help( m_move_down_button, shared_help, "move_garrison_to_adjacent" );

	// create lordship (nobility) potrait / button
	layer = m_interface->find( "lord_portrait" );
	point = layer->get_rect().top_left();
	m_lord_button = new t_button( point, this );
	m_lord_button->set_click_handler( discard_argument< t_button * >( bound_handler( *this, &t_town_window::choose_lord ) ) );
	m_lord_portrait = new t_bitmap_layer_window( NULL, t_screen_point(0,0), m_lord_button );
	m_lord_button->set_released_image( m_lord_portrait );
	set_help( m_lord_button, help, "governer" );

	layer = m_interface->find( "lord_border" );
	m_lord_border = new t_bitmap_layer_window( layer, t_screen_point(0,0), this );


	create_dwelling_icons();
}

// --------------------------------------------------------------------
// town screen
// create building image
// --------------------------------------------------------------------

void t_town_window::create_building_image( t_town_building building, int layout_bottom )
{
	t_town_type town_type = m_town->get_type();
	t_building_traits const& traits = get_traits( town_type, building );

	if (traits.keyword == 0)
		return;

	std::string shadow_name = traits.keyword;

	shadow_name += " shadow";

	t_bitmap_layer const* layer = m_layout->find( traits.keyword );
	t_bitmap_layer const* highlight_layer = m_highlight_layout->find( traits.keyword );
	t_bitmap_layer const* shadow_layer = m_layout->find( shadow_name );
	
	if (layer == 0)
	{
		fatal_error( format_string( "Could not find layer %s", traits.keyword ) );
		return;
	}

	t_screen_rect  rect = layer->get_rect();

	if (highlight_layer != 0)
		rect = get_extent( rect, highlight_layer->get_rect() );

	t_screen_point origin = rect.top_left();
	t_screen_point backdrop_origin = to_client( m_terrain_background->get_screen_rect().top_left() );
	t_button*      button;

	button = new t_button( origin, m_terrain_background );

	button->set_visible( false );

	m_building_button[building] = button;
	m_building_window[building] = new t_blended_bitmap_window( layer, -origin, button, true, 15 );
	button->set_released_image( m_building_window[building] );
	button->set_help_balloon_text( traits.name );

	if (shadow_layer != 0)
	{
		m_building_shadow[building] = new t_blended_bitmap_window( shadow_layer, 
			                                                        t_screen_point(0,0),
			                                                        m_terrain_background );
		m_building_shadow[building]->set_visible( false );
		m_building_shadow[building]->move_to_back();
	}

	if (highlight_layer != 0)
	{
		m_building_highlight[building] = new t_bitmap_layer_window( highlight_layer, -origin,
			                                                        button );
		button->set_highlighted_image( m_building_highlight[building] );
	}
}

// --------------------------------------------------------------------
// town screen
// create windows for buildings
// --------------------------------------------------------------------
void t_town_window::create_building_windows()
{
	int         i;
	t_town_type town_type = m_town->get_type();
	int         layout_bottom = m_layout->get_rect().bottom;

	// close old windows.
	for (i = 0; i < k_town_building_count; i++)
	{
		if (m_building_button[i] == 0)
			continue;
		m_building_button[i]->close(); // also closes animation, if any.
		if (m_building_shadow[i] != 0)
			m_building_shadow[i]->close();
		m_building_animation[i] = 0;
		m_building_button[i] = 0;
		m_building_shadow[i] = 0;
	}

	// create all town building buttons
	t_town_drawing_order const& order = get_drawing_order( town_type );

	for (i = 0; i < order.count; i++)
		create_building_image( order.order[i], layout_bottom );

	t_button_handler handler;

	// Default for all buildings without other hooks is just a simple 
	// description of the building
	for (i = 0; i < k_town_building_count; i++)
	{
		if (m_building_button[i] != 0)
		{
			m_building_button[i]->set_click_handler( add_2nd_argument( 
				bound_handler( *this, &t_town_window::describe_building ), t_town_building(i) ));
			m_building_button[i]->set_right_click_handler( add_2nd_argument( 
				bound_handler( *this, &t_town_window::describe_building ), t_town_building(i) ));
		}
	}

	
	// add the hooks for the various builings
	// start with town hall
	for (i = k_town_village_hall; i <= k_town_city_hall; i++)
		if (m_building_button[i] != 0)
			m_building_button[i]->set_click_handler( bound_handler( *this,
			                                         &t_town_window::buy_building ) );

	m_building_button[k_town_tavern]->set_click_handler( bound_handler( *this, 
													     &t_town_window::hire_hero ));
	if (m_building_button[k_town_shipyard] != 0)
	{
		handler = bound_handler( *this, &t_town_window::build_ship );
		m_building_button[k_town_shipyard]->set_click_handler( handler );
	}
	for (i = 0; i < 5; i++)
		if (m_building_button[k_town_mage_guild_1 + i] != 0)
			m_building_button[k_town_mage_guild_1 + i]->set_click_handler(
				bound_handler( *this, &t_town_window::mage_guild ));
	for (i = 0; i < 2; i++)
		if (m_building_button[k_town_library_1 + i] != 0)
			m_building_button[k_town_library_1 + i]->set_click_handler(
				bound_handler( *this, &t_town_window::mage_guild ));

	// hooks for creature dwellings
	for (i = k_town_dwelling_1; i <= k_town_dwelling_8; i++)
	{
		if (m_building_button[i] != 0)
		{
			t_building_traits const& traits = get_traits( town_type, t_town_building(i));

			m_building_button[i]->set_click_handler( add_2nd_argument( 
				bound_handler( *this, &t_town_window::buy_creatures ), traits.creature) );
		}
	}
    // Blacksmith
	if (m_building_button[k_town_blacksmith] != 0)
		m_building_button[k_town_blacksmith]->set_click_handler(
			bound_handler( *this, &t_town_window::blacksmith ));
	// creature portal
	if (m_building_button[k_town_portal] != 0)
		m_building_button[k_town_portal]->set_click_handler(
			bound_handler( *this, &t_town_window::creature_portal_click ));
    // Castle building window (fort, citadel, castle)
	for (i = k_town_fort; i <= k_town_castle; i++)
		if (m_building_button[i] != 0)
			m_building_button[i]->set_click_handler( bound_handler( *this,
			                                         &t_town_window::castle ) );
    // Prison.
	if (m_building_button[k_town_prison] != 0)
		m_building_button[k_town_prison]->set_click_handler(
			bound_handler( *this, &t_town_window::prison));
    // Skeleton transformer.
	if (m_building_button[k_town_skeleton_transformer] != 0)
		m_building_button[k_town_skeleton_transformer]->set_click_handler(
			bound_handler( *this, &t_town_window::skeleton_transformer));
    // Seminary
	if (m_building_button[k_town_seminary] != 0)
    {
		m_building_button[k_town_seminary]->set_click_handler(
			bound_handler( *this, &t_town_window::seminary ));
    }
    // University
	if (m_building_button[k_town_university] != 0)
    {
		m_building_button[k_town_university]->set_click_handler(
			bound_handler( *this, &t_town_window::university ));
    }
	// Treasury
	if (m_building_button[k_town_treasury] != 0)
    {
		m_building_button[k_town_treasury]->set_click_handler(
			bound_handler( *this, &t_town_window::describe_treasury ));
    }
	// Mana vortex
	if (m_building_button[k_town_mana_vortex] != 0)
    {
		m_building_button[k_town_mana_vortex]->set_click_handler(
			bound_handler( *this, &t_town_window::mana_vortex ));
    }
	// Caravan
	if (m_building_button[k_town_caravan] != 0)
	{
		m_building_button[k_town_caravan]->set_click_handler(
			discard_argument< t_button * >( bound_handler( *this, &t_town_window::display_caravan_window ) ) );
	}
}

// --------------------------------------------------------------------
// town screen
// create windows for buildings
// --------------------------------------------------------------------
void t_town_window::create_buildings()
{
	// set which buildings are drawn
	int i;

	m_drawn_buildings = m_town->get_buildings();
	m_fading_in.clear();
	m_fading_out.clear();

	// eliminate buildings which are replaced by others.
	for (i = 0; i < k_town_building_count; i++)
		if (m_drawn_buildings[i])
			m_drawn_buildings &= ~get_replaced_buildings(t_town_building(i));

	// make constructed buildings visible
	for (i = 0; i < k_town_building_count; i++)
	{
		t_window* button = m_building_button[i];

		if (button == 0)
			continue;

		if (m_drawn_buildings[i])
		{
			fade_in( t_town_building(i), true );
		}
		else
		{
			t_window* shadow_window = m_building_shadow[i];

			button->set_visible( false );
			if (shadow_window != 0)
				shadow_window->set_visible( false );
		}
	}
}

// --------------------------------------------------------------------
// town screen
// create detail windows for individual creatures
// --------------------------------------------------------------------
void t_town_window::create_creature_detail()
{
	t_bitmap_layer const* layer;
	t_window*             window;
	t_screen_point        interface_origin = get_interface_origin();
	t_screen_point        creature_text_origin;
	t_screen_point        creature_icon_origin;

	layer = m_interface->find( "statistics_scroll" );
	if (layer == 0)
		m_creature_text_detail = 0;
	else
	{
		m_creature_text_detail = new t_bitmap_layer_window( layer, -interface_origin, m_interface_window );
		creature_text_origin = layer->get_rect().top_left();
	
	    t_help_block const&     shared_help_block = get_help_block( "shared" );
        layer = m_interface->find( "health_icon" );
        window = new t_bitmap_layer_window( layer, -creature_text_origin, m_creature_text_detail );
	    set_help( window, shared_help_block, "hit_points" );
        layer = m_interface->find( "damage_icon" );
        window = new t_bitmap_layer_window( layer, -creature_text_origin, m_creature_text_detail );
	    set_help( window, shared_help_block, "damage" );
        layer = m_interface->find( "move_icon" );
        window = new t_bitmap_layer_window( layer, -creature_text_origin, m_creature_text_detail );
   	    set_help( window, shared_help_block, "movement" );
        layer = m_interface->find( "mana_icon" );
        window = new t_bitmap_layer_window( layer, -creature_text_origin, m_creature_text_detail );
	    set_help( window, shared_help_block, "spell_points" );
	}

	layer = m_interface->find( "skill_icon_background" );
	if (layer == 0)
		m_creature_icon_detail = 0;
	else
	{
		m_creature_icon_detail = new t_bitmap_layer_window( layer, -interface_origin, m_interface_window );
		creature_icon_origin = layer->get_rect().top_left();
	}
	m_creature_detail.create( m_interface, m_creature_text_detail, creature_text_origin,
		                      m_creature_icon_detail, creature_icon_origin );
}


// --------------------------------------------------------------------
// town screen
// create detail windows for individual creatures
// --------------------------------------------------------------------
void t_town_window::set_detail_mode( bool text_mode )
{
	m_showing_text_detail = text_mode;
	if (m_creature_text_detail != 0)
		m_creature_text_detail->set_visible( text_mode );
	if (m_creature_icon_detail != 0)
		m_creature_icon_detail->set_visible( !text_mode );
}

// --------------------------------------------------------------------
// town screen
// create detail windows for individual creatures
// --------------------------------------------------------------------
void t_town_window::show_icon_detail( t_button* button )
{
	set_detail_mode( false );
}

// --------------------------------------------------------------------
// town screen
// create detail windows for individual creatures
// --------------------------------------------------------------------
void t_town_window::show_text_detail( t_button* button )
{
	set_detail_mode( true );
}

// --------------------------------------------------------------------
// town screen
// close garrison windows
// --------------------------------------------------------------------
void t_town_window::close_garrison_window()
{
	t_army* adjacent_army = m_adventure_frame->create_army( m_town,
		                                                    m_adjacent_army, m_temp_army );
	m_trading_armies[ m_town ] = adjacent_army;

	m_town->set_visiting_army( adjacent_army );

	m_adjacent_army = 0;

	if (m_garrison_window != 0)
	{
		m_garrison_window->close();
		m_garrison_window = 0;
	}
	if (m_visitor_window != 0)
	{
		m_visitor_window->close();
		m_visitor_window = 0;
	}
	if (m_split_button)
	{
		m_split_button->close();
		m_split_button = NULL;
	}
	if (m_single_border)
	{
		m_single_border->close();
		m_single_border = NULL;
	}
}

// --------------------------------------------------------------------
// town screen
// get origin, in client coordinates, of interface window
// --------------------------------------------------------------------
t_screen_point t_town_window::get_interface_origin() const
{
	return to_client( m_interface_window->get_screen_rect().top_left() );
}


// --------------------------------------------------------------------
// town screen
// create windows for armies
// --------------------------------------------------------------------
void t_town_window::create_garrison_window()
{
	t_screen_rect					  rect;
	t_creature_array_window::t_layout first_row;
	t_handler						  change_handler;
	t_adventure_map &				  map = *m_town->get_map();
	const t_screen_point			  interface_origin = get_interface_origin();
	const t_bitmap_layer const*		  box = m_interface->find( "army_display" );
	const int						  player_number = map.get_player_number();

	bool							  foreigners = ( m_town->get_owner_number() != player_number );

	setup_temp_army();

	if (m_adjacent_army == 0)
	{
		first_row = t_creature_array_window::k_single_row;

		// Place the special top border piece
    	t_bitmap_layer const* layer = m_interface->find( "single_border" );
    	m_single_border = new t_bitmap_layer_window( layer, -interface_origin, m_interface_window );

		// Place the single ring split button
	    rect = m_interface->find( "single_split_button" )->get_rect() - interface_origin;
	    m_split_button = new t_toggle_button( g_divide_single_button.get(), rect.top_left(), m_interface_window );
	}
	else
	{
		first_row = t_creature_array_window::k_top_row;

		if ( m_adjacent_army->get_owner_number() != player_number )
			foreigners = true;

		// Place the double ring split button
        rect = m_interface->find( "split_button" )->get_rect() - interface_origin;
	    m_split_button = new t_toggle_button( g_divide_button.get(), rect.top_left(), m_interface_window );
	}

    // Finish setting up the split button
	t_help_block const& help = get_help_block( "town_screen" );
	set_help( m_split_button, help, "split" );
  m_split_button->set_click_handler( bound_handler( *this, &t_town_window::divide_clicked ) );	

	rect = box->get_rect() - interface_origin;

	change_handler = bound_handler( *this, &t_town_window::set_dwelling_icons );
	m_garrison_window = new t_creature_array_window( rect.top_left(), &m_town->get_garrison(),
                                                     first_row, 0, m_interface_window );
	m_garrison_window->set_select_handler( bound_handler( m_creature_detail, 
		                                   &t_creature_detail_display::select_creature ) );
	m_garrison_window->select_first();
	m_garrison_window->set_change_handler( change_handler );

	m_garrison_window->set_double_click_handler( bound_handler( *this,
													&t_town_window::creature_double_click ));

	if ( foreigners )
		setup_foreign_creature_array_window_controls( map, m_garrison_window );
	else
		clear_foreign_creature_array_window_controls( m_garrison_window );


	t_screen_rect  top_rect = m_interface_window->to_client( m_garrison_window->get_screen_rect() );
	t_screen_point bottom;
	
	bottom.x = top_rect.left;
	bottom.y = top_rect.top + m_garrison_window->get_frame_height();

	m_move_up_button->set_visible( m_adjacent_army != 0 );
	m_move_down_button->set_visible( m_adjacent_army != 0 );
	if (m_adjacent_army == 0)
	{
		rect += t_screen_point(0, (rect.bottom - bottom.y) / 2);
		m_garrison_window->move( rect );
		return;
	}

	m_visitor_window = new t_creature_array_window( bottom, &m_adjacent_army->get_creatures(),
                                                     t_creature_array_window::k_bottom_row,
												     0, m_interface_window );
	m_visitor_window->move_before( m_garrison_window );
	m_visitor_window->set_select_handler( bound_handler( m_creature_detail, 
		                                  &t_creature_detail_display::select_creature ) );
	m_visitor_window->set_change_handler( change_handler );

	m_visitor_window->set_double_click_handler( bound_handler( *this,
												&t_town_window::creature_double_click ));

	if ( foreigners )
	{
		setup_foreign_creature_array_window_controls( map, m_visitor_window );
		m_move_up_button->enable( m_adjacent_army->get_owner_number() == player_number );
		m_move_down_button->enable( m_town->get_owner_number() == player_number );
	}
	else
	{
		clear_foreign_creature_array_window_controls( m_visitor_window );
		m_move_up_button->enable( true );
		m_move_down_button->enable( true );
	}

	m_garrison_window->link_selection( m_visitor_window );

	if (m_garrison_window->get_selected_slot() < 0)
	{
		m_visitor_window->select_first();
		m_keyboard_on_visitor = true;
	} 
	else
	{
		m_keyboard_on_visitor = false;
	}
}

// --------------------------------------------------------------------
// town screen
// --------------------------------------------------------------------
void t_town_window::close_click( t_button* )
{
	// Cancel any in progress drag operation
	cancel_drag();

	create_army();
	m_town->update_state();
	m_adventure_frame->update_funds();
	close();
}

// ----------------------------------------------------
// town screen
// show army dialog
// ----------------------------------------------------
void t_town_window::creature_double_click( t_creature_array_window* window, int slot )
{
	t_window* dialog;


	if (m_adjacent_army || m_garrison_window->get_army())
    {
		dialog = new t_army_dialog_town( m_town, m_adjacent_army, m_adventure_frame, window->get_army(), m_town, slot );
    }
    else
        return;

	dialog->run_modal();
	if (m_garrison_window)
	    m_garrison_window->update();
	if (m_visitor_window)
	    m_visitor_window->update();
}

// --------------------------------------------------------------------
// town screen
// --------------------------------------------------------------------
void t_town_window::move_down_click( t_button* )
{
//	assert( m_visitor_window != 0 );
	assert( m_garrison_window != 0 );
	// try and add source to dest, preferably in the same slot.
	if( m_visitor_window != 0 )
		m_visitor_window->merge( m_garrison_window );
}

// --------------------------------------------------------------------
// town screen
// --------------------------------------------------------------------
void t_town_window::move_up_click( t_button* )
{
//	assert( m_visitor_window != 0 );
	assert( m_garrison_window != 0 );
	// try and add source to dest, preferably in the same slot.
	if( m_visitor_window != 0 )
		m_garrison_window->merge( m_visitor_window );
}

// --------------------------------------------------------------------
// town screen
// show creature portal dialog
// --------------------------------------------------------------------
static t_external_string const k_text_no_room( "no_room.town" );
bool check_garrison_space( t_town* town, t_creature_type creature_type )
{
	if (town->get_garrison().can_add( creature_type ))
		return true;

	std::string text;

	text = replace_keywords( k_text_no_room, "%creatures", 
		                     get_traits( creature_type ).plural_name );
	ok_dialog( text );
	return false;
}

void t_town_window::creature_portal_click( t_button* )
{
	t_counted_ptr<t_dialog_creature_portal> dialog;
	t_player const&							player = m_town->get_map()->get_player();
	t_material_array const&					funds = player.get_funds();
	t_creature_type							creature_type = m_town->get_portal_creature();

	if ( !player_owns_town() )
	{
		ok_dialog( k_buy_creatures_not_owner, true );
		return;
	}

	if (!check_garrison_space( m_town, creature_type ))
		return;

	dialog = new t_dialog_creature_portal( this, m_town, funds, get_recruitment_discount() );

	if (!dialog->run_modal())
	{
	    set_dwelling_icons();
		return;
	}

	creature_type = dialog->get_creature_type();
	if (!check_garrison_space( m_town, creature_type ))
		return;
	m_town->use_portal( dialog->get_amount() );
	m_town->check_bonus_buildings(m_adjacent_army, this);
	m_garrison_window->update();
	m_material_display.update();
	set_dwelling_icons();
	m_garrison_window->select_creature( m_town->get_garrison().find( creature_type ) );
}

// --------------------------------------------------------------------
// town screen
// --------------------------------------------------------------------
void t_town_window::build_all()
{
	if ( !player_owns_town() )
	{
		ok_dialog( k_buy_building_not_owner, true );
		return;
	}

	t_town_building building;

	// add grail
	m_town->create_building( k_town_grail );
	fade_in( k_town_grail, true );
	while (true)
	{
		for (building = t_town_building(0); building < k_town_building_count; enum_incr( building ))
		{
			if (m_town->can_build( building ))
				break;
		}
		if (building == k_town_building_count)
		{
			m_town_list->update();
			set_dwelling_icons();
			if (m_adjacent_army != 0)
				m_adjacent_army->get_creatures().learn_spells( m_town );

			m_town->check_bonus_buildings(m_adjacent_army, this);
	
			if (m_garrison_window)
				m_garrison_window->update();

			if (m_visitor_window)
				m_visitor_window->update();

			// check_bonus_buildings might have given a hero a new level
			update_nobility_display();

			return;
		}
		m_town->create_building( building );
		fade_in( building, true );
	}
}

// --------------------------------------------------------------------
// town screen
// --------------------------------------------------------------------
void t_town_window::menu_buy_building()
{
	buy_building( 0 );
}

// --------------------------------------------------------------------
// town screen
// --------------------------------------------------------------------
void t_town_window::divide_clicked( t_button* button )
{
	if (button->is_pressed())
	{
		m_garrison_window->set_divide_mode( bound_handler( *this, 
		                                    &t_town_window::cancel_divide_mode ));
		if (m_visitor_window != 0)
		{
			m_visitor_window->set_divide_mode( bound_handler( *this,
											   &t_town_window::cancel_divide_mode ));
		}
	}
	else
	{
		cancel_divide_mode();
	}
}

// --------------------------------------------------------------------
// town screen
// --------------------------------------------------------------------
void t_town_window::cancel_divide_mode()
{
	m_split_button->set_pressed( false );
	m_garrison_window->cancel_divide_mode();
	if (m_visitor_window != 0)
		m_visitor_window->cancel_divide_mode();
}

// --------------------------------------------------------------------
// town screen
// --------------------------------------------------------------------
static t_external_string const k_text_purchase_building( "town_purchase_building.misc" );
static t_external_string const k_text_hire_creature( "town_hire_creature.misc" );
static t_external_string const k_text_hire_hero( "town_hire_hero.misc" );
static t_external_string const k_text_mage_guild( "town_mage_guild.misc" );
static t_external_string const k_text_use_blacksmith( "town_use_blacksmith.misc" );
static t_external_string const k_text_prison( "town_prison.misc" );
static t_external_string const k_text_skeleton_transformer( "town_skeleton_transformer.misc" );
static t_external_string const k_text_seminary( "town_seminary.misc" );
static t_external_string const k_text_university( "town_university.misc" );
static t_external_string const k_text_build_ship( "town_build_ship.misc" );
static t_external_string const k_text_choose_lord( "town_choose_lord.misc" );
static t_external_string const k_text_create_caravan( "town_create_caravan.misc" );
static t_external_string const k_text_view_caravans( "town_view_caravans.misc" );
static t_external_string const k_text_handle_arriving_caravans( "town_handle_arriving_caravans.misc" );
extern t_external_string const k_text_marketplace;
static t_external_string const k_text_build_all_buildings( "town_build_all_buildings_cheat" );

void t_town_window::town_menu( t_button* button )
{
	t_scroll_menu*   menu = new t_scroll_menu( this );
	t_handler        handler;
	std::string    text;

	handler = bound_handler( *this, &t_town_window::menu_buy_building );
	menu->add_item( k_text_purchase_building, handler);
	handler = add_argument<t_button*>( bound_handler( *this, &t_town_window::castle ), 0 );
	menu->add_item( k_text_hire_creature, handler );
	if (m_town->has( k_town_tavern ))
	{
		handler = add_argument<t_button*>(bound_handler( *this, &t_town_window::hire_hero ), 0);
		menu->add_item( k_text_hire_hero, handler );
	}
	if (m_town->has( k_town_mage_guild_1 ))
	{
		handler = add_argument<t_button*>(bound_handler( *this, &t_town_window::mage_guild ), 0);
		menu->add_item( k_text_mage_guild, handler );
	}
	if (m_town->has( k_town_blacksmith ))
	{
		text = replace_keywords( k_text_use_blacksmith, "%blacksmith",
			                     get_traits( m_town->get_type(), k_town_blacksmith ).name );
		handler = add_argument<t_button*>(bound_handler( *this, &t_town_window::blacksmith ), 0);
		menu->add_item( text, handler );
	}
	if (m_town->has( k_town_prison ))
	{
		handler = add_argument<t_button*>(bound_handler( *this, &t_town_window::prison), 0);
		menu->add_item( k_text_prison, handler );
	}
	if (m_town->has( k_town_skeleton_transformer ))
	{
		handler = add_argument<t_button*>(bound_handler( *this, &t_town_window::skeleton_transformer ), 0);
		menu->add_item( k_text_skeleton_transformer, handler );
	}
	if (m_town->has( k_town_seminary ))
	{
		handler = add_argument<t_button*>(bound_handler( *this, &t_town_window::seminary ), 0);
		menu->add_item( k_text_seminary, handler );
	}
	if (m_town->has( k_town_university ))
	{
		handler = add_argument<t_button*>(bound_handler( *this, &t_town_window::university ), 0);
		menu->add_item( k_text_university, handler );
	}
	if (m_town->has( k_town_shipyard ))
	{
		handler = add_argument<t_button*>( bound_handler( *this, &t_town_window::build_ship ), 0 );
		menu->add_item( k_text_build_ship, handler );
	}
	if ( m_town->has( k_town_caravan ) )
	{
		handler = bound_handler( *this, &t_town_window::caravan_create );
		menu->add_item( k_text_create_caravan, handler );
		handler = bound_handler( *this, &t_town_window::caravan_view );
		menu->add_item( k_text_view_caravans, handler );
		handler = bound_handler( *this, &t_town_window::caravan_handle_arrivals );
		menu->add_item( k_text_handle_arriving_caravans, handler );
	}

	handler = bound_handler( *this, &t_town_window::choose_lord );
	menu->add_item( k_text_choose_lord, handler );

	t_adventure_map& map = *m_town->get_map();

	if ( map.get_player_number() == map.get_current_player_number())
	{
		handler = bound_handler( *this, &t_town_window::marketplace );
		menu->add_item( k_text_marketplace, handler );
	}

	if (show_cheat_menus())
	{
		handler = bound_handler( *this, &t_town_window::build_all );
		menu->add_item( k_text_build_all_buildings, handler );
	}
	menu->open( to_client( button->get_screen_rect() ).top_left() );
}

void t_town_window::marketplace()
{
	m_adventure_frame->marketplace();
	update_materials();
}

// --------------------------------------------------------------------
// create an animation window
// --------------------------------------------------------------------
static t_window* create_animation_window( t_abstract_cache<t_animation>* cache, 
										  t_button* button,
										  t_bitmap_layer_window* building_window,
										  t_screen_point origin )
{
	// animations get created one of 4 ways:
	// if image has a base frame:
	//		transparent frame, containing base image from animation, plus animated window
	// if image does not have a base frame, but animation extends beyond layout still:
	//		transparent frame, containing base image from layout, plus animated window
	// if image does not have a base frame, and animation does not extend beyond layout still:
	//	    no frame, animated window parented directly to the layout still
	t_animation_ptr		  animation		 = cache->get();
	t_bitmap_layer const* first_frame	 = animation->get_frame( 0 );
	t_bitmap_layer const* position_frame = animation->front().get();
	t_screen_rect		  animation_rect = animation->get_rect();
	t_window*             frame_window   = building_window;
	t_screen_rect         frame_rect;

	// compute screen coordinates of entire animation
	frame_rect = animation_rect - position_frame->get_rect().top_left();
	frame_rect = building_window->to_screen( frame_rect );
	// shift them to client coordinates for button
	frame_rect = button->to_client( frame_rect );

	// check if animation contains a base frame
	if (first_frame->get_height() > 0)
	{
		t_bitmap_layer const* base_frame = (animation->begin() + 1)->get();

		frame_window = new t_window( frame_rect, t_window::k_completely_transparent,
			                         button );

		origin = -animation_rect.top_left();
		building_window->set_visible( false );
		t_window* base_window = new t_bitmap_layer_window( base_frame, origin, 
				                                           frame_window );
	}
	else
	{
		// see if we can fit the animation as a child of the building_window
		t_screen_rect still_rect = building_window->get_screen_rect();

		still_rect = button->to_client( still_rect );
		if (!still_rect.contains( frame_rect ))
		{
			t_screen_rect rect = get_extent( still_rect, frame_rect );

			frame_window = new t_window( frame_rect, t_window::k_completely_transparent,
			                             button );
			origin = -animation_rect.top_left() - rect.top_left() + frame_rect.top_left();
			building_window->set_visible( false );
			t_window* base_window = new t_bitmap_layer_window( building_window->get_bitmap(),
				                                               origin, frame_window );
		}
	}

	// create actual animated sequence
	t_animated_window* window = new t_animated_window( animation, origin, 100, 
		                                               frame_window );
	return frame_window;
}

// --------------------------------------------------------------------
// add animation for a given building
// --------------------------------------------------------------------
bool t_town_window::add_animation( t_town_building building )
{
	if (m_building_animation[building] != 0)
		return false;

	t_scaled_animation_cache cache;
	t_town_type              town_type = m_town->get_type();
	t_layout_cache           layout_cache = get_layout_cache( town_type );
	t_building_traits const& traits = get_traits( town_type, building );
	t_screen_point           unscaled_origin = layout_cache.get_unscaled_origin( traits.keyword );
	t_animation_ptr			 animation;

	cache = get_animation( town_type, building );
	if (!cache.is_valid())
		return false;

	cache.set_origin( unscaled_origin );
	animation = cache.get();

	if (animation == 0)
		return false;

	t_window*				 window;
	t_button*				 button = m_building_button[building];
	t_bitmap_layer_window*   building_window = m_building_window[building];
	t_screen_point           building_origin = building_window->get_screen_rect().top_left();
	
	building_origin = m_terrain_background->to_client( building_origin );

	window = create_animation_window( &cache, button, m_building_window[building],
		                              -building_origin );
	m_building_animation[building] = window;
	if (window != m_building_window[building])
		button->set_released_image( window );

	window = create_animation_window( &get_highlight_animation( town_type, building ), 
		                              button, m_building_highlight[building],
									  -building_origin );
	m_highlight_animation[building] = window;
	if (window != m_building_highlight[building])
		button->set_highlighted_image( window );
	return true;
}

// --------------------------------------------------------------------
// town screen
// present the town hall screen.
// --------------------------------------------------------------------
void t_town_window::buy_building( t_button* )
{
	t_window_ptr    dialog = new t_buy_building_window( m_town, this );
	t_town_building result;

	result = t_town_building( dialog->run_modal() );
	if (result == k_town_no_building)
		return;

	get_adventure_sound( k_adventure_sound_town_build )->play( get_sound_volume() );

	m_town->buy_building( result );
	if (m_adjacent_army != 0)
		m_adjacent_army->get_creatures().learn_spells( m_town );
	fade_in( result, false );
	m_material_display.update();
	m_town_list->update();
	set_dwelling_icons();
	m_town->check_bonus_buildings(m_adjacent_army, this);
	m_garrison_window->update();

	if (m_visitor_window != 0)
		m_visitor_window->update();

	// Check bonus buildings might have given a hero a level
	update_nobility_display();
}

// --------------------------------------------------------------------
// town screen
// present the town hall screen.
// --------------------------------------------------------------------
void t_town_window::fade_in( t_town_building building, bool fast )
{
	m_drawn_buildings.set( building );
	if (fast)
		m_building_window[building]->set_alpha( 15 );
	else
		m_building_window[building]->set_alpha( 0 );
	m_building_button[building]->set_visible( true );
	if (m_building_shadow[building] != 0)
	{
		if (fast)
			m_building_shadow[building]->set_alpha( 15 );
		else
			m_building_shadow[building]->set_alpha( 0 );
		m_building_shadow[building]->set_visible( true );
	}
	m_fading_in.insert( building );

	// fade out buildings replaced by this one.
	t_town_building index;
	
	for (index = t_town_building(0); index < k_town_building_count; enum_incr(index) )
	{
		if (get_replaced_buildings(building)[index] && m_drawn_buildings[index])
		{
			t_window* shadow_window = m_building_shadow[index];

			if (fast)
			{
				m_building_button[index]->set_visible( false );
				if (shadow_window != 0)
					shadow_window->set_visible( false );
			}
			else
				m_fading_out.insert( index );
			m_drawn_buildings.reset( index );

			if (!m_fading_in.count(index))
				continue;

			m_fading_in.erase( index );
			m_building_button[index]->set_visible( false );
			if (shadow_window != 0)
				shadow_window->set_visible( false );
		}
	}
}

// --------------------------------------------------------------------
// town screen
// present the town hall screen.
// --------------------------------------------------------------------
void t_town_window::do_fade()
{
	t_blended_bitmap_window* window;
	t_blended_bitmap_window* shadow_window;
	t_town_building          building;
	bool                     animation_loaded = false;
	t_set::iterator          index = m_fading_in.begin();

	while (index != m_fading_in.end())
	{
		building = *index++;
		window = m_building_window[building];

		if (window->get_alpha() < 15)
		{
			window->set_alpha( window->get_alpha() +  1 );
			shadow_window = m_building_shadow[building];
			if (shadow_window != 0)
				shadow_window->set_alpha( shadow_window->get_alpha() + 1 );
		}
		if (window->get_alpha() == 15)
		{
			if (!animation_loaded)
			{
				animation_loaded = add_animation( building );
				m_fading_in.erase( building );
			}
		}
	}
	index = m_fading_out.begin();
	while (index != m_fading_out.end())
	{
		building = *index++;
		window = m_building_window[building];
		window->set_alpha( window->get_alpha() - 1 );
		shadow_window = m_building_shadow[building];
		if (shadow_window != 0)
			shadow_window->set_alpha( shadow_window->get_alpha() - 1 );
		if (window->get_alpha() > 0)
		{
			m_fading_out.erase( building );
			m_building_button[building]->set_visible( false );
			if (shadow_window != 0)
				shadow_window->set_visible( false );
		}
	}

}

// --------------------------------------------------------------------
// town screen
// determine discount, if any, for creatures due to Penny Pinching artifact
// --------------------------------------------------------------------
int t_town_window::get_recruitment_discount() const
{
	int discount;
	int visitor_discount;

	// compute gold discount due to artifacts, if any
	discount = m_town->get_garrison().get_recruitment_discount();
	if (m_adjacent_army != 0)
	{
		visitor_discount = m_adjacent_army->get_creatures().get_recruitment_discount();
		if (discount < visitor_discount)
			discount = visitor_discount;
	}
	return discount;
}

// --------------------------------------------------------------------
// town screen
// buy creatures from a dwelling
// --------------------------------------------------------------------
void t_town_window::buy_creatures( t_button* button, t_creature_type creature_type )
{
	t_counted_ptr<t_recruit_dialog> dialog;
	t_player const&					player = m_town->get_map()->get_player();
	t_material_array const&			funds = player.get_funds();
	int                             limit = m_town->get_population( creature_type );
	int								gold_cost = -1;
	int							    discount;

	if ( !player_owns_town() )
	{
		ok_dialog( k_buy_creatures_not_owner, true );
		return;
	}

	if (!check_garrison_space( m_town, creature_type ))
		return;

	discount = get_recruitment_discount();
	gold_cost = (get_traits( creature_type ).cost[k_gold] * (100 - discount) + 50) / 100;

	dialog = new t_recruit_dialog( this, creature_type, limit, funds, gold_cost );

	if (!dialog->run_modal())
	{
		return;
	}

	m_town->hire_creatures( creature_type, dialog->get_amount() );
	m_garrison_window->update();
	m_town->check_bonus_buildings(m_adjacent_army, this);
	m_garrison_window->update();
	m_material_display.update();
	set_dwelling_icons();
	m_garrison_window->select_creature( m_town->get_garrison().find( creature_type ) );
}

// --------------------------------------------------------------------
// town screen
// buy hero
// --------------------------------------------------------------------
static t_external_string const k_text_tavern_empty( "town_tavern_empty.misc" );
static t_external_string const k_text_tavern_tomorrow( "town_tavern_tomorrow.misc" );
static t_external_string const k_buy_heroes_not_owner( "town.buy_heroes_not_owner" );

void t_town_window::hire_hero( t_button* )
{
	if ( !player_owns_town() )
	{
		ok_dialog( k_buy_heroes_not_owner, true );
		return;
	}

	if (m_town->get_tavern_delay() > 0)
	{
		std::string text;

		if (m_town->get_tavern_delay() == 1)
			text = k_text_tavern_tomorrow;
		else
			text = k_text_tavern_empty;
		text = replace_keywords( text, "%days", 
			                     format_string( "%i", m_town->get_tavern_delay() ) );
		ok_dialog( text, true );
		return;
	}

	t_counted_ptr<t_hire_hero_dialog> dialog;
	t_adventure_map&                  map = *m_town->get_map();
	t_player&						  player = *m_town->get_owner();
	int                               funds = player.get_funds()[k_gold];
	int                               hire_cost[k_hero_class_basic_count];

	get_hero_hiring_cost( m_town->get_type(), hire_cost );
	if (!m_town->get_garrison().can_add_hero())
		funds = 0;

	dialog = new t_hire_hero_dialog( hire_cost, funds, map );
	dialog->set_cursor( get_cursor( false ));
	if (!dialog->run_modal())
	{
		return;
	}

	// hire hero.
	t_default_hero data = dialog->get_data();
	t_hero*        hero = map.create_hero( data, get_alignment( data.hero_class ) );

	player.spend( k_gold, hire_cost[data.hero_class] );
	hero->learn_spells( m_town );
	m_town->get_garrison().add( hero );
	m_garrison_window->select_creature( m_town->get_garrison().find( hero ) );
	m_garrison_window->update();
	m_material_display.update();
	set_dwelling_icons();
	m_town->set_tavern_delay( k_town_tavern_delay );

	m_town->check_bonus_buildings(m_adjacent_army, this);
	m_garrison_window->update();

	update_nobility_display();
}

// -----------------------------------------------------------------------
// setup a possible newly-created adjacent army
// -----------------------------------------------------------------------
void t_town_window::setup_temp_army()
{
	if (m_adjacent_army != 0)
		return;

	if ( !m_town->get_map()->get_player().can_create_army() )
		return;

	t_adv_map_point point;

	m_temp_army->get_creatures().clear();
	m_temp_army->set_owner( m_town->get_map()->get_player_number() );

	if (	!m_town->find_adjacent_space( point, *m_temp_army )
		&&	!m_town->find_nearby_space( point, *m_temp_army ) )
	{
		return;
	}

	m_adjacent_army = m_temp_army;
}

// --------------------------------------------------------------------
// town screen
// create new army
// --------------------------------------------------------------------
void t_town_window::create_army()
{
	t_army* adjacent_army = m_adventure_frame->create_army( m_town, m_adjacent_army, m_temp_army );

	if ( adjacent_army )
		m_adjacent_army = adjacent_army; // Keep record for use by trigger_temporary_army_events()

	m_town->set_visiting_army( adjacent_army );
}

// ----------------------------------------------------
// town screen
// handle a menu click
// ----------------------------------------------------
bool t_town_window::menu_click( int id )
{
	if (!is_visible())
		return false;

	switch (id)
	{
		case k_id_cheat_materials:
			m_town->get_map()->add_free_materials();
			m_material_display.update();
			return true;

		case k_id_cheat_build_all_buildings:
			{
				build_all();
			}
			return true;
	}
	return t_window::menu_click( id );
}

// ----------------------------------------------------
// town screen
// compute time required to load background and layout
// ----------------------------------------------------
int t_town_window::get_load_time( t_town* town )
{
	t_town_type			  old_type	  = k_town_type_count;
	t_terrain_type		  old_terrain = k_terrain_count;
	t_town_type			  type		  = town->get_type();
	t_terrain_type		  terrain	  = town->get_terrain();
	int                   result	  = 0;

	if (m_town != 0)
	{
		old_type = m_town->get_type();
		old_terrain = m_town->get_terrain();
	}

	if (old_type != type)
	{
		result += get_layout_cache( type ).get_load_cost();
	}
	if (old_type != type || old_terrain != terrain)
	{
		result += get_backdrop( type, terrain, get_scale_factor() ).get_load_cost();
	}
	return result;
}

// ----------------------------------------------------
// Check bonus buildings and the like for the town
// we just entered
// ----------------------------------------------------
void t_town_window::enter_town()
{
	m_town->visit_town( m_town );
	if ( m_adjacent_army )
		m_adjacent_army->visit_town( m_town );

	update_nobility_display();

	check_for_grail();

	// Grail can affect bonus buildings so do that first
	m_town->check_bonus_buildings(m_adjacent_army, this);
	
	update_creature_windows();

	// In case hero gained a level from bonus buildings 
	update_nobility_display();
}

// ----------------------------------------------------
// town screen
// set a new town to display
// ----------------------------------------------------
void t_town_window::set_new_town( t_town* town, t_progress_handler* progress_handler )
{
	t_town_type			  old_type	= k_town_type_count;
	t_terrain_type		  old_terrain = k_terrain_count;
	t_town_type			  type		= town->get_type();
	t_terrain_type		  terrain		= town->get_terrain();

	if (m_town != 0)
	{
		old_type = m_town->get_type();
		old_terrain = m_town->get_terrain();
		close_garrison_window();
	
		t_army_map::iterator index = m_trading_armies.find( town );
	
		if (index != m_trading_armies.end())
			m_adjacent_army = index->second;

		if (m_adjacent_army == 0)
		{
			t_visiting_army_selector selector;

			m_adjacent_army = selector.choose_visiting_army( town );
		}
		m_adventure_frame->select_town( town );
	}
	m_town = town;
	m_town_name->set_text( town->get_name() );
	create_garrison_window();
	set_dwelling_icons();
	if (old_type != type)
	{
		m_layout = get_layout_cache( type ).get( progress_handler );
		m_highlight_layout = get_highlight( type );
	}
	if (old_type != type || old_terrain != terrain)
	{
		m_terrain = get_backdrop( type, terrain, get_scale_factor()).get( progress_handler );

		// get interface section
		t_window*                window;
		t_bitmap_group::iterator terrain_index = m_terrain->begin();
		t_bitmap_layer const*    backdrop = terrain_index->get();
		t_bitmap_layer const* layer;
		t_screen_point        point;

		terrain_index++;
		// place terrain background image.
		layer = m_interface->find( "town_image" );
		point.y = layer->get_rect().bottom - backdrop->get_height();
		point.x = 0;
		if (m_terrain_background != 0)
			m_terrain_background->set_bitmap( backdrop );
		else
		{
			m_terrain_background = new t_bitmap_layer_window( backdrop, point, this );
			m_terrain_background->move_to_back();
		}

		// create building windows
		if (old_type != type)
			create_building_windows();

		// close old foreground images
		t_window_list::iterator foreground_window = m_terrain_foreground.begin();

		while (foreground_window != m_terrain_foreground.end())
		{
			(*foreground_window)->close();
			foreground_window++;
		}

		// place terrain foreground images
		m_terrain_foreground.clear();
		while (terrain_index != m_terrain->end())
		{
			window = new t_bitmap_layer_window( terrain_index->get(), t_screen_point(0,0),
												m_terrain_background );
			m_terrain_foreground.push_back( window );
			terrain_index++;
		}
	}
	create_buildings();
}
 
// ----------------------------------------------------
// town screen
// set a new town to display
// ----------------------------------------------------
void t_town_window::set_town( t_town* town )
{
	if (town == m_town)
		return;

	t_progress_bar_dialog* dialog = new t_progress_bar_dialog( this );
	t_progress_handler*    handler = dialog->get_handler();

	t_game_application::set_cursor_visible( false );
	handler->increment_maximum( get_load_time( town ));
	set_new_town( town, handler );
	t_game_application::set_cursor_visible( true );
	dialog->close();

	enter_town();
}

// ----------------------------------------------------
// town screen
// create icons for purchasing creatures
// ----------------------------------------------------
void t_town_window::create_dwelling_icons()
{
	t_screen_point		  interface_origin = get_interface_origin();
	int					  i;
	t_bitmap_layer const* layer;
	t_screen_point        point;
	t_bitmap_layer const* inset;
	t_bitmap_layer const* inset_text;
	t_screen_point        inset_offset;
	t_screen_rect         inset_text_rect;

	m_rings = g_creature_rings.get();
	inset_offset = m_rings->find( "portrait" )->get_rect().top_left();
	inset = m_rings->find( "inset" );
	inset_text = m_rings->find( "inset_text" );
	inset_text_rect = inset_text->get_rect() - inset->get_rect().top_left();
	for (i = 0; i < k_dwelling_icon_count; i++)
	{
		layer = m_interface->find( format_string( "dwelling_%i", i + 1 ) );
		if (layer == 0)
			continue;

		t_dwelling_icon& icon = m_dwelling_icons[i];

		point = layer->get_rect().top_left() - interface_origin;
		icon.button = new t_button( point, m_interface_window );
		icon.portrait = new t_bitmap_layer_cache_window( t_bitmap_layer_ptr(),
											             t_screen_point(0,0), icon.button );
		icon.button->set_released_image( icon.portrait );
	}

	layer = m_interface->find( "creature_portrait_rings" );
	if (layer != 0)
	{
		t_window* window;

		window = new t_bitmap_layer_window( layer, -interface_origin, m_interface_window );
	}

	for (i = 0; i < k_dwelling_icon_count; i++)
	{
		t_dwelling_icon& icon = m_dwelling_icons[i];
		
		if (icon.portrait == 0)
			continue;

		point = m_interface_window->to_client( icon.button->get_screen_rect().top_left() );
		icon.inset = new t_bitmap_layer_window( inset, point - inset_offset, m_interface_window );
		icon.inset->set_visible( false );
		icon.text = new t_text_window( get_font( inset_text_rect.height() ), inset_text_rect,
			                           icon.inset, "", t_pixel_24(0,0,0) );
		icon.text->set_center_horizontal();
		icon.text->set_drop_shadow( true, t_pixel_24(200,200,200));
	}

}

// ----------------------------------------------------
// town screen
// create icons for purchasing creatures
// ----------------------------------------------------
static t_external_string const k_text_purchase_creature( "purchase_creature.town" );

static void add_creature_icon( t_town_window::t_dwelling_icon& icon, t_creature_type creature,
							   int growth, int population )
{
	if (icon.portrait == 0)
		return;

	// set creature
	icon.creature = creature;
	// set image of creature
	icon.portrait->set_bitmap( get_creature_portrait( icon.creature, 52 ));
	// set text saying "purchase creature"
	std::string text;

	text = format_string( "%i", (growth + ( t_town::k_population_divisor / 2 ) ) / t_town::k_population_divisor );
	text = replace_keywords( k_text_purchase_creature, "%creature_name", 
			                 get_creature_name( icon.creature, 2, false ),
							 "%growth", text );
	icon.portrait->set_help_balloon_text( text );
	// set label with # of creatures available.
	icon.inset->set_visible( true );
	icon.text->set_text( abbreviate_number( population, *icon.text->get_font(), 
											icon.text->get_width() ));
}

void t_town_window::set_dwelling_icons()
{
	int			    icon_number = 0;
	t_town_building dwelling;
	t_town_type     town_type = m_town->get_type();
	std::string     text;
    int             i;

	for (i = 0; i < k_dwelling_icon_count; i++)
	{
		t_dwelling_icon&	 icon = m_dwelling_icons[i];
		if (icon.portrait == 0)
			continue;
		icon.button->set_visible( false );
	    icon.inset->set_visible( false );
    }

	// hooks for creature dwellings
	for (dwelling = k_town_dwelling_1; dwelling <= k_town_dwelling_8; enum_incr(dwelling))
	{
		if (!m_town->has( dwelling ))
			continue;

		// get creature for this dwelling
		t_building_traits const& traits = get_traits( town_type, dwelling );
		t_dwelling_icon&		 icon = m_dwelling_icons[icon_number++];

		if (icon.portrait == 0)
			continue;

		// set creature
		add_creature_icon( icon, traits.creature, m_town->get_growth( dwelling ),
						   m_town->get_population( traits.creature ) );
		icon.button->set_click_handler( add_2nd_argument( bound_handler( *this, 
								        &t_town_window::buy_creatures ), traits.creature) );
		icon.button->set_right_click_handler( add_2nd_argument( bound_handler( *this, 
											  &t_town_window::display_growth_breakdown_window ), dwelling ) );
		icon.button->set_visible( true );
	}

	// add creature portal
	if (!m_town->has( k_town_portal ))
		return;

	t_dwelling_icon& icon = m_dwelling_icons[5];

	if (icon.portrait == 0)
		return;

	add_creature_icon( icon, m_town->get_portal_creature(), 
		               m_town->get_growth( k_town_portal ),
		               m_town->get_portal_population() );

	// set function to purchase creature
	icon.button->set_click_handler( bound_handler( *this, 
									&t_town_window::creature_portal_click ));
	icon.button->set_right_click_handler( add_2nd_argument( bound_handler( *this, 
										  &t_town_window::display_growth_breakdown_window ), k_town_portal ) );
	icon.button->set_visible( true );
}

// ----------------------------------------------------
// town screen
// display mage guild
// ----------------------------------------------------
static t_external_string const k_no_mage_guild( "no_mage_guild.misc" );

void t_town_window::mage_guild( t_button* )
{
	if ( m_town->has( k_town_mage_guild_1 ) )
	{
		t_mage_guild_window* guild = new t_mage_guild_window( m_town, m_adjacent_army, this );
		guild->run_modal();
	}
	else
	{
		ok_dialog( k_no_mage_guild, true );
	}
}

// ----------------------------------------------------
// town screen
// display blacksmith
// ----------------------------------------------------
static t_external_string const k_text_no_armies_for_blacksmith( "no_armies_for_blacksmith.text" );
void t_town_window::blacksmith( t_button* )
{
	if ( m_garrison_window->get_army()->empty() && ((m_visitor_window == NULL) || m_visitor_window->get_army()->empty()) )
    {
	    ok_dialog( k_text_no_armies_for_blacksmith );
        return;
    }
	
    t_blacksmith_window* blacksmith_ptr = new t_blacksmith_window( m_town, m_adjacent_army, m_adventure_frame, this );

	blacksmith_ptr->run_modal();

    // Update display.
    update_creature_windows();

	m_material_display.update();
}

static t_external_string const k_castle_no_dwellings( "town.castle_no_dwellings" );

// ----------------------------------------------------
// town screen
// display castle
// ----------------------------------------------------
void t_town_window::castle( t_button* )
{
	// See if we have any creature sources at all
	t_town_building dwelling;
	bool have_creature_source = m_town->has( k_town_portal );
	for ( dwelling = k_town_dwelling_1; dwelling <= k_town_dwelling_8; enum_incr(dwelling) )
	{
		if (m_town->has( dwelling ))
		{
			have_creature_source = true;
			break;
		}
	}
	
	if (have_creature_source) {

		// Recruit creatures.
		t_counted_ptr<t_castle_window> castle_ptr;

		castle_ptr = new t_castle_window( m_town, this, get_recruitment_discount() );
		castle_ptr->run_modal();

		// Update display.
		m_town->check_bonus_buildings(m_adjacent_army, this);
		update_creature_windows();
		set_dwelling_icons();
		m_material_display.update();

	} else {

		// Display a helpful message
		ok_dialog( k_castle_no_dwellings, true );

	}
}

// ----------------------------------------------------
// town screen
// Right-click display of where-the-growth-is-coming-from
// window
// ----------------------------------------------------
void t_town_window::display_growth_breakdown_window(t_button *button, t_town_building building)
{
	m_town->display_growth_breakdown_window(building, this);
}

// Clicked on skeleton transformer.
void t_town_window::skeleton_transformer( t_button* button )
{
	t_counted_ptr<t_skeleton_transformer_window> transformer_window_ptr = 
            new t_skeleton_transformer_window ( this, m_town, m_adjacent_army );

	transformer_window_ptr->run_modal();
    update_creature_windows();
}


// ----------------------------------------------------
// ----------------------------------------------------
void t_town_window::on_close()
{
	if( g_cheats_window != 0 && g_cheats_window->is_open() )
		g_cheats_window->close();
	g_cheats_window = 0;
}

// Clicked on prison.
void t_town_window::prison( t_button* )
{
	// Just in case we had nowhere to put them when the town was actually taken
	if ( same_team( m_town->get_owner(), &m_town->get_map()->get_player() ) )
		m_town->free_prisoners();

	t_counted_ptr<t_town_prison_window> prison_window_ptr = new t_town_prison_window( m_town, m_adventure_frame, this );

	prison_window_ptr->run_modal();
}

// ----------------------------------------------------
// town screen
// display seminary
// ----------------------------------------------------
void t_town_window::seminary( t_button* )
{
	t_building_traits const& traits = get_traits( m_town->get_type(), k_town_seminary );
    t_seminary_window* seminary_ptr = new t_seminary_window( m_town, m_adjacent_army, 
									*m_town, traits.name, m_town->get_school_skills(), this ); 

	seminary_ptr->run_modal();

    // Update display.
	m_material_display.update();
}

// ----------------------------------------------------
// town screen
// display university
// ----------------------------------------------------
void t_town_window::university( t_button* )
{
	t_building_traits const& traits = get_traits( m_town->get_type(), k_town_university );
    t_seminary_window* seminary_ptr = new t_seminary_window( m_town, m_adjacent_army, 
									*m_town, traits.name, m_town->get_school_skills(), this ); 

	seminary_ptr->run_modal();

    // Update display.
	m_material_display.update();
}

// ----------------------------------------------------
// town screen
// display shipyard
// ----------------------------------------------------
static t_external_string const k_text_no_place_for_boat( "town_no_place_for_boat.misc" );
static t_external_string const k_text_no_money_for_boat( "town_no_money_for_boat.misc" );
static t_external_string const k_text_buy_boat( "town_buy_boat.misc" );
static t_external_string const k_text_ship_built( "town_ship_built.misc" );

void t_town_window::build_ship( t_button* )
{
	// cost of 1 boat
	t_material_array				boat_cost;
	std::string						text;
	t_counted_ptr<t_basic_dialog>	dialog;
	bool							can_buy = false;
	t_adv_map_point					position;
	t_player*						owner = m_town->get_owner();

	dialog = new t_basic_dialog;
	boat_cost[k_gold] = 1000;
	boat_cost[k_wood] = 10;
	if (!m_town->find_new_ship_position( position, false ))
	{
		text = k_text_no_place_for_boat;
	}
	else
	{
		dialog->add_materials( boat_cost );
		if (owner->get_funds() < boat_cost)
		{
			text = k_text_no_money_for_boat;
		} 
		else
		{
			text = k_text_buy_boat;
			dialog->add_cancel_button();
			can_buy = true;
		}
		text = replace_keywords( text, boat_cost );
	}
	dialog->add_ok_button();
	dialog->set_text( text );
	dialog->open();
	if (!dialog->run_modal())
	{
		return;
	}

	if (!can_buy)
		return;
	m_town->build_ship();
	owner->spend( boat_cost );
	m_material_display.update();
	ok_dialog( k_text_ship_built );
}

// ----------------------------------------------------
// town screen
// Update the garrison & visitor displays
// ----------------------------------------------------
void t_town_window::update_creature_windows()
{
	if (m_garrison_window)
		m_garrison_window->update();
	if (m_visitor_window)
		m_visitor_window->update();
}

// ----------------------------------------------------
// town screen
// Display a simple description of a building, for 
// buildings with nothing else to do for a left-click
// ----------------------------------------------------
void t_town_window::describe_building(t_button *button, t_town_building building)
{
	t_building_traits const& traits = get_traits( m_town->get_type(), building);

	t_counted_ptr<t_basic_dialog> dialog = new t_basic_dialog(this);

	dialog->set_text( traits.help_text );
	dialog->add_ok_button();

	dialog->set_title( traits.name );
	
	dialog->open();
	dialog->run_modal();
}

static t_external_string const k_treasury_left_click( "treasury_left_click.town" );

// ----------------------------------------------------
// town screen
// Describe the treasury. Adds a little more information
// than a normal description
// ----------------------------------------------------
void t_town_window::describe_treasury( t_button *button )
{
	t_building_traits const& traits = get_traits( m_town->get_type(), k_town_treasury );

	t_counted_ptr<t_basic_dialog> dialog = new t_basic_dialog(this);

	std::string text = replace_keywords( k_treasury_left_click, m_town->get_last_treasury_bonus(), k_gold );

	dialog->set_text( text );
	dialog->add_ok_button();

	dialog->set_title( traits.name );
	
	dialog->open();
	dialog->run_modal();
}

static t_external_string const k_town_mana_vortex_no_heroes( "mana_vortex.no_heroes.town" );
static t_external_string const k_town_mana_vortex_already_used_plural( "mana_vortex.already_used_plural.town" );
static t_external_string const k_town_mana_vortex_already_used_singular( "mana_vortex.already_used_singular.town" );
static t_external_string const k_town_mana_vortex_already_full_plural( "mana_vortex.already_full_plural.town" );
static t_external_string const k_town_mana_vortex_already_full_singular( "mana_vortex.already_full_singular.town" );
static t_external_string const k_town_mana_vortex_initial( "mana_vortex.initial.town" );
static t_external_string const k_town_mana_vortex_accepted( "mana_vortex.accepted.town" );
static t_external_string const k_town_mana_vortex_not_owner( "mana_vortex.not_owner.town" );

// ----------------------------------------------------
// town screen
// Handle a click on the mana vortex
// ----------------------------------------------------
void t_town_window::mana_vortex( t_button* button )
{
	int                     i;
	t_creature_array        garrisoned_hero_array;
	t_creature_array        visiting_hero_array;
	bool					any_garrison_creatures = false;
	bool					any_visiting_creatures = false;
	bool				    any_heroes = false;
	bool					any_useable_heroes = false;
	std::vector< t_hero * > full_heroes;
	
	t_building_traits const& traits = get_traits( m_town->get_type(), k_town_mana_vortex );

	if ( !player_owns_town() )
	{
		ok_dialog( k_town_mana_vortex_not_owner, true );
		return;
	}

    // Can only be used by 1 hero in a 1 week period.
	if (m_town->get_mana_vortex_delay() > 0)
    {
	    std::string status_text;
		
		if ( m_town->get_mana_vortex_delay() == 1 )
			status_text = k_town_mana_vortex_already_used_singular;
		else
			status_text = k_town_mana_vortex_already_used_plural;

		status_text = replace_keywords( status_text, "%days_remaining", format_string( "%d", m_town->get_mana_vortex_delay() ) );
        ok_dialog( status_text, true );
        return;
    }

    // Check for heroes.
	for (i = 0; i < t_creature_array::k_size; i++)
	{
		if ( m_town->get_garrison()[i].get_number() > 0 )
			any_garrison_creatures = true;

		t_hero* hero = m_town->get_garrison()[i].get_hero();
		if (hero == 0)
			continue;

		any_heroes = true;

		if ( hero->get_spell_points() >= (2 * hero->get_maximum_spell_points() ) )
		{
			full_heroes.push_back( hero );
		}
		else
		{
			any_useable_heroes = true;

			t_creature_stack_ptr hero_stack_ptr = hero;

			garrisoned_hero_array.swap( hero_stack_ptr, i );
		}
	}

	if ( m_adjacent_army )
	{
		for (i = 0; i < t_creature_array::k_size; i++)
		{
			if ( m_adjacent_army->get_creatures()[i].get_number() > 0 )
				any_visiting_creatures = true;

			t_hero* hero = m_adjacent_army->get_creatures()[i].get_hero();
			if (hero == 0)
				continue;

			any_heroes = true;

			if ( hero->get_spell_points() >= (2 * hero->get_maximum_spell_points() ) )
			{
				full_heroes.push_back( hero );
			}
			else
			{
				any_useable_heroes = true;
				
				t_creature_stack_ptr hero_stack_ptr = hero;

				visiting_hero_array.swap( hero_stack_ptr, i );
			}
		}
	}

	if ( !any_garrison_creatures && !any_visiting_creatures )
	{
		// Just describe building
		describe_building( button, k_town_mana_vortex );
		return;
	}

    if (any_heroes == false)
    {
		// No heroes
        ok_dialog( k_town_mana_vortex_no_heroes, true );
        return;
    }

    if (any_useable_heroes == false)
    {
		// All heroes at 2 * max already
	    std::string status_text;
		
		if ( full_heroes.size() == 1 )
			status_text = k_town_mana_vortex_already_full_singular;
		else
			status_text = k_town_mana_vortex_already_full_plural;
		
		status_text = replace_keywords( status_text, full_heroes );
        ok_dialog( status_text, true );
        return;
    }

    // Select hero for benefit.
	t_counted_ptr<t_dialog_mana_vortex> dialog_ptr;

	dialog_ptr = new t_dialog_mana_vortex( this );
	dialog_ptr->init_dialog( &garrisoned_hero_array,
							 m_adjacent_army ? &visiting_hero_array : NULL,
							 traits.name, 
							 k_town_mana_vortex_initial, 
							 k_town_mana_vortex_accepted );

    // Did hero get benefit?
	if (dialog_ptr->run_modal())
	{
	    m_town->use_up_mana_vortex();
		update_creature_windows();
	}
}

static t_external_string const k_grail_offer_title("grail_offer.title");
static t_external_string const k_grail_offer_text("grail_offer.text");
static t_external_string const k_grail_refused_title("grail_refused.title");
static t_external_string const k_grail_refused_text("grail_refused.text");
static t_external_string const k_grail_already_built_title("grail_already_built.title");
static t_external_string const k_grail_already_built_text("grail_already_built.text");

// ----------------------------------------------------------------------
// Checks to see if anyone in town has the grail. If so, and the 
// grail building can be built, offers to build it.
// If the grail building CANNOT be built in this town, a message box will
// appear to that effect.
// Always checks the garrison. Checks the adjacent_army too, if not NULL
// ----------------------------------------------------------------------
void t_town_window::check_for_grail( )
{
	if ( m_town->get_owner_number() != m_town->get_map()->get_player_number() )
		return; // Can only build grail building in own town

	t_artifact const * grail = NULL;

	t_creature_stack * grail_holder = NULL;
	bool in_backpack;
	int grail_slot;

	t_creature_array    *creature_arrays[2];

	creature_arrays[0] = m_town;

	if (m_adjacent_army)
	{
		creature_arrays[1] = &m_adjacent_army->get_creatures();
	}
	else
	{
		creature_arrays[1] = NULL;
	}

	int array;
	for (array = 0; grail == NULL && array < ELEMENTS_OF(creature_arrays); array++)
	{
		if (creature_arrays[array])
		{
			if ( creature_arrays[array]->get_owner_number() != m_town->get_map()->get_player_number() )
				continue; // Can't steal other people's grail
			
			int creature_index;

			for (creature_index = 0; grail == NULL && creature_index < t_creature_array::k_size; creature_index++)
			{
				t_creature_stack & creature = (*creature_arrays[array])[creature_index];

				// Scan the backpack
				int count = creature.get_backpack_count();
				int i;

				for ( i = 0; grail == NULL && i < count; i++ )
				{
					t_artifact const & artifact = creature.get_backpack_slot( i );

					if ( artifact.has_effect( k_artifact_effect_grail_building ) )
					{
						// Found it 
						grail = &artifact;
						grail_holder = &creature;
						in_backpack = true;
						grail_slot = i;
					}
				}

				// Scan the equipped stuff too
				t_artifact_slot slot;
				for ( slot = t_artifact_slot(0); grail == NULL && slot < k_artifact_slot_hero_count; enum_incr( slot ) )
				{
					t_artifact const & artifact = creature.get_artifact( slot );

					if ( artifact.has_effect( k_artifact_effect_grail_building ) )
					{
						// Found it 
						grail = &artifact;
						grail_holder = &creature;
						in_backpack = false;
						grail_slot = slot;
					}
				}
			}
		}
	}

	if ( grail )
	{
		t_hero_keyword_replacer replacer;
		replacer.add_artifact( *grail );
		
		replacer.add_keyword( "%building_name", get_traits( m_town->get_type(), k_town_grail ).name );

		// Don't bother the user with repeated failure messages 
		bool checked_already = ( m_checked_grail_towns.find( m_town ) != m_checked_grail_towns.end() );
		m_checked_grail_towns.insert( m_town );

		// Can we build the grail building?
		// NOTE: Not affected by normal one-building-per-day rule.
		if ( m_town->has( k_town_grail ) )
		{
			if ( !checked_already )
				ok_dialog( replacer( k_grail_already_built_text ), true, replacer( k_grail_already_built_title ) );
		}
		else if ( !m_town->is_enabled( k_town_grail, false ) )
		{
			if ( !checked_already )
				ok_dialog( replacer( k_grail_refused_text ), true, replacer( k_grail_refused_title ) );
		}
		else
		{
			t_counted_ptr< t_basic_dialog > dialog = new t_basic_dialog( this );

			dialog->set_title( replacer( k_grail_offer_title ) );
			dialog->set_text( replacer( k_grail_offer_text ) );
			dialog->add_ok_button();
			dialog->add_cancel_button();
			dialog->open();
			if ( dialog->run_modal() )
			{
				// Build building & take away artifact
				m_town->create_building( k_town_grail );
				fade_in( k_town_grail, false );
				if ( in_backpack )
				{
					grail_holder->remove_backpack( grail_slot );
				}
				else
				{
					t_hero * hero = grail_holder->get_hero();
					assert( hero ); 
					if ( hero )
					{
						hero->remove( t_artifact_slot( grail_slot ) );
					}
				}
			}
		}
	}
}


static t_external_string const k_lord_portrait_help_balloon( "town_lord_portrait_help_balloon.misc" );

// ----------------------------------------------------------------------
// Updates the display of the current ruler of this town
// ----------------------------------------------------------------------
void t_town_window::update_nobility_display()
{
	t_hero * ruler = m_town->get_best_nobility_hero();

	if ( ruler == NULL || ruler->get_skill( k_skill_nobility ) == k_mastery_none )
	{
		// Town does not have a ruler
		m_lord_border->set_visible( false );
		m_lord_button->set_visible( false );
	}
	else
	{
		m_lord_border->set_visible( true );

		t_bitmap_layer_ptr portrait = ruler->get_portrait();

		m_lord_portrait->set_bitmap( portrait );

		t_hero_keyword_replacer replacer;

		replacer.add_hero( ruler );
		replacer.add_keyword( "%skill", get_name( t_skill( k_skill_nobility, ruler->get_skill( k_skill_nobility ) ) ) );
		replacer.add_keyword( "%town_name", m_town->get_name() );

		m_lord_button->set_help_balloon_text( replacer( k_lord_portrait_help_balloon ) );
		m_lord_button->set_visible( true );
	}
}

static t_external_string const k_no_lords( "town.no_lords" );
extern t_external_string const k_choose_lord_title;
static t_external_string const k_choose_lord_not_owner( "town.choose_lord_not_owner" );
// ----------------------------------------------------------------------
// Display choose lord dialog when lord portrait is clicked
// ----------------------------------------------------------------------
void t_town_window::choose_lord()
{
	if ( m_town->get_owner_number() != m_town->get_map()->get_player_number() )
	{
		ok_dialog( k_choose_lord_not_owner, true );
		return;
	}

	std::vector< t_hero * > nobles;

	// Add anyone in the current town with nobility
	t_creature_array    *creature_arrays[2];

	creature_arrays[0] = m_town;

	if (m_adjacent_army)
	{
		creature_arrays[1] = &m_adjacent_army->get_creatures();
	}
	else
	{
		creature_arrays[1] = NULL;
	}

	int array;
	for (array = 0; array < ELEMENTS_OF(creature_arrays); array++)
	{
		if (creature_arrays[array])
		{
			if ( creature_arrays[array]->get_owner_number() != m_town->get_owner_number() )
				continue;

			int creature_index;

			for (creature_index = 0; creature_index < t_creature_array::k_size; creature_index++)
			{
				t_hero * hero = (*creature_arrays[array])[creature_index].get_hero();

				if (   hero 
					&& hero->get_skill_power( k_skill_nobility) > 0 
					&& !hero->is_dead() )
				{
					nobles.push_back( hero );
				}
			}
		}
	}

	if ( nobles.empty() )
	{
		ok_dialog( k_no_lords, true, k_choose_lord_title );
		return;
	}

	t_counted_ptr< t_dialog_choose_lord > dialog = new t_dialog_choose_lord( nobles, m_town, this );

	if ( dialog->run_modal() )
		update_nobility_display();
}

// ----------------------------------------------------------------------
// Get the visiting or trading army for a town
// ----------------------------------------------------------------------
t_army_ptr t_town_window::get_trading_army( t_town * town )
{
	if ( town == m_town )
		return m_adjacent_army;

	t_army_map::iterator index = m_trading_armies.find( town );

	if (index != m_trading_armies.end())
		return index->second;

	// Pull in army if need be
	t_visiting_army_selector selector;

	t_army_ptr result = selector.choose_visiting_army( town );

	return result;	
}

// ----------------------------------------------------------------------
// Set the visiting or trading army for a town
// ----------------------------------------------------------------------
void t_town_window::set_trading_army( t_town * town, t_army_ptr army )
{
	if ( town == m_town )
	{
		m_adjacent_army = army;
	}
	else
	{
		t_army_map::iterator index = m_trading_armies.lower_bound( town );

		if ( index != m_trading_armies.end() && index->first == town && index->second == army )
			return;

		t_army_map::value_type val( town, army );

		index = m_trading_armies.insert( index, val );

		// In case value was already in trading armies...
		index->second = army;
	}
}

static t_external_string const k_no_caravan_building( "caravan.no_caravan_building" );

// ----------------------------------------------------------------------
// Display the basic caravan window, which leads to the various caravan
// dialogs
// ----------------------------------------------------------------------
void t_town_window::display_caravan_window()
{
	if ( !m_town->has( k_town_caravan ) )
	{
		ok_dialog( k_no_caravan_building, true );
		return;
	}

	int dialog_result;

	{
		t_counted_ptr< t_dialog_caravan_initial > dialog;

		dialog = new t_dialog_caravan_initial( this );
		
		dialog_result = dialog->run_modal();
	} // Destroy dialog 

	switch( dialog_result )
	{
		case t_dialog_caravan_initial::k_result_create:
			caravan_create();
			break;

		case t_dialog_caravan_initial::k_result_view:
			caravan_view();
			break;
		
		case t_dialog_caravan_initial::k_result_arrivals:
			caravan_handle_arrivals();
			break;
	}
	m_material_display.update();
}

// ----------------------------------------------------------------------
// Show the caravan creation dialog
// ----------------------------------------------------------------------
void t_town_window::caravan_create()
{
	t_counted_ptr< t_dialog_caravan_creation > dialog;

	dialog = new t_dialog_caravan_creation( m_town->get_map(), this, m_town );

	if ( !dialog->init_dialog() )
		return;

	dialog->run_modal();

	// Dialog may have rearranged this town's armies, or even created or destroyed
	// armies on the adventure map. Refresh creature rings -- may even need to 
	// switch from double to single or from single to double
	close_garrison_window();
	
	t_army_map::iterator index = m_trading_armies.find( m_town );

	if (index != m_trading_armies.end())
		m_adjacent_army = index->second;

	create_garrison_window();
}

// ----------------------------------------------------------------------
// Show the caravan handle-arrivals dialog
// ----------------------------------------------------------------------
void t_town_window::caravan_handle_arrivals()
{
	t_counted_ptr< t_dialog_caravan_arrivals > dialog = new t_dialog_caravan_arrivals( this, *m_town->get_map() );

	if ( dialog->init_dialog( *m_town, m_adjacent_army.get() ) )
	{
		dialog->run_modal();

		enter_town();
	}
}

// ----------------------------------------------------------------------
// Show the caravan view dialog
// ----------------------------------------------------------------------
void t_town_window::caravan_view()
{
	t_counted_ptr< t_dialog_caravan_view > dialog = new t_dialog_caravan_view( this, *m_town->get_map() );

	if ( dialog->init_dialog( m_town ) )
	{
		dialog->run_modal();
	}
}

// -----------------------------------------------------------------------
// Does the person viewing this window actually own the town?
// -----------------------------------------------------------------------
bool t_town_window::player_owns_town() const
{
	t_adventure_map &map = *m_town->get_map();
	int player_number = map.get_player_number();

	return player_number == m_town->get_owner_number();
}

// -----------------------------------------------------------------------
// Trigger all the events for the temporary armies we just placed on 
// the map
// -----------------------------------------------------------------------
void t_town_window::trigger_temporary_army_events()
{
	// Note that if an army already existed on the map at its current location, it will
	// not retrigger events if we call trigger_events()
	if ( m_adjacent_army )
	{
		m_adjacent_army->trigger_events();
	}

	t_army_map::iterator it;

	for ( it = m_trading_armies.begin(); it != m_trading_armies.end(); it++ )
	{
		if ( m_town->get_map()->is_game_over() )
			return;

		if ( it->second && !it->second->empty() )
		{
			it->second->trigger_events();
		}
	}
}
	
void t_town_window::update_materials()
{
    m_material_display.update();
}
